#include "echapper.hpp"

#include <QVector>

#include "carte.hpp"

#include <iomanip>
#include <vector>
#include <string>

#define LAST(tab) tab[tab.size() - 1]

std::ostream & make_empty_scenario(std::ostream & out)
{
  out<<"#Tout ce qui suit un dièse est un commentaire."<<std::endl<<std::endl
     <<"# Si un argument contient un caractère spécial "<<std::endl
     <<"# (\\, #, espace, retour à la ligne, ...) vous pouvez l'échapper avec "
     <<std::endl<<"# un '\\' devant. Exemple : un\\ seul\\ argument."<<std::endl
     <<"#Voici les différentes prises : "<<std::endl
     <<"#0 -> passe, 1 -> prise, 2 -> garde, "<<std::endl
     <<"#3 -> garde sans, 4 -> garde contre"<<std::endl<<std::endl
     <<"#Voici les différentes cartes : "<<std::endl;
  std::vector<std::vector<std::string> > piles(6);
  unsigned int plus_grand_nom = 0;
  for(unsigned int couleur = 0 ; couleur < 4 ; couleur++)
    {
      for(unsigned int valeur = 0 ; valeur < 14 ; valeur++)
	{
	  Carte c(static_cast<Carte::Valeur>(valeur),
		  static_cast<Carte::Couleur>(couleur));
	  std::string str = c.nom();
	  if(str.size() > plus_grand_nom)plus_grand_nom = str.size();
	  piles[couleur].push_back(str);
	}
    }
  for(unsigned int i = PETIT ; i < 79 ; i++)
    {
      std::string str = Carte(i).nom();
      if(str.size() > plus_grand_nom)plus_grand_nom = str.size();
      if(i <= PETIT + 10)
	piles[4].push_back(str);
      else
	piles[5].push_back(str);
    }
  /* 3 colonnes par ligne */
  for(unsigned colonne = 0 ; colonne < piles.size() ; colonne +=3)
    {
      for(unsigned int ligne = 0 ;
	  ligne < piles[colonne].size()
	    || (colonne + 1 < piles.size()
		&& ligne < piles[colonne + 1].size())
	    || (colonne + 2 < piles.size()
		&&ligne < piles[colonne + 2].size()) ;
	  ligne++)
	{
	  out<<"# "<<std::setw(plus_grand_nom);
	  if(ligne < piles[colonne].size())
	    {
	      out<<piles[colonne][ligne]<<" ";
	    }
	  else
	    {
	      out<<" "<<" "; /*1er " " : prend tout l'espace. */
	    }
	  out<<std::setw(plus_grand_nom);
	  if(colonne + 1 < piles.size()
	     && ligne < piles[colonne + 1].size())
	    {
	      out<<piles[colonne + 1][ligne]<<" ";
	    }
	  else
	    {
	      out<<" "<<" ";
	    }
	  out<<std::setw(plus_grand_nom);
	  if(colonne + 2 < piles.size()
	     && ligne < piles[colonne + 2].size())
	    {
	      out<<piles[colonne + 2][ligne];
	    }
	  else
	    {
	      out<<" ";
	    }
	  out<<std::endl;
	}
      out<<std::endl;
    }
  out<<"# Voici les événements que vous pouvez attendre : "<<std::endl;
  out<<"# identifier nom\\ exact # J'attends que le client s'identifie."
     <<std::endl
     <<"# inviter adversaire\\ 1 adv\\ 2 3 4"<<std::endl
     <<"# J'attends que le client invite exactement ces quatre personnes."
     <<std::endl
     <<"# priser x # J'attends que le client prise : 0 -> passe etc."
     <<std::endl
     <<"# appeler x # J'attends que le client appelle une carte."
     <<std::endl
     <<"# ecarter x y z # J'attends que le client fasse cet écart."
     <<std::endl
     <<"# jouer x # J'attends que le client joue cette carte."
     <<std::endl
     <<"# NB : Lorsqu'il y a plusieurs arguments (inviter, ecarter),"
     <<std::endl
     <<"# l'ordre compte."
     <<std::endl
     <<std::endl
     <<"# Voici les événements que vous pouvez émettre : "<<std::endl
     <<"# erreur_protocole # J'informe le client qu'il a fait une erreur."
     <<std::endl
     <<"# refuse # J'informe le client que son action n'est pas valable."
     <<std::endl
     <<"# entree nom # J'informe le client que nom est disponible."
     <<std::endl
     <<"# sortie nom # nom n'est plus disponible."
     <<std::endl
     <<"# noms joueur1 j2 j3 j4 j5"
     <<std::endl
     <<"# J'informe le client que la partie fait jouer ces 5 joueurs dans"
     <<std::endl
     <<"# cet ordre."
     <<std::endl
     <<"# numero x # J'informe le client que son numéro de tour change."
     <<std::endl
     <<"# distribution a b c d e f g h i j k l m n o"
     <<std::endl
     <<"# J'informe le client qu'il dispose de ces 15 cartes."
     <<std::endl
     <<"# contrat x # J'informe le client que le joueur suivant fait une enchère."
     <<std::endl
     <<"# appel # J'informe le client qu'il doit appeler une carte."
     <<std::endl
     <<"# contrat_final x y z # J'informe le client que x fait l'enchère y"
     <<std::endl
     <<"# en appelant la carte z."
     <<std::endl
     <<"# chien x y z # J'informe le client que le chien est dévoilé."
     <<std::endl
     <<"# atout a b c # J'informe le client que l'écart contient un ou plusieurs"
     <<std::endl
     <<"# atout(s)."
     <<std::endl
     <<"# jeu # J'informe le client que le jeu commence."
     <<std::endl
     <<"# carte x # J'informe le client que le joueur suivant joue x."
     <<std::endl
     <<"# pli y # J'informe le client que le joueur y remporte le pli."
     <<std::endl
     <<"# resultat a b c d e # J'informe le client des résultats de chacun."
     <<std::endl<<std::endl;
  out<<"# Voici un exemple :"<<std::endl
     <<"# Les joueurs à la table sont truc, toto, machin, bidule, chose,"
     <<std::endl
     <<"# et vous jouez toto. Vous devez inviter les autres."<<std::endl
     <<" identifier toto"<<std::endl
     <<" entree truc"<<std::endl
     <<" entree Guignol"<<std::endl
     <<" entree chose"<<std::endl
     <<" entree machin"<<std::endl
     <<" entree bidule"<<std::endl
     <<" entree toto"<<std::endl
     <<" sortie Guignol"<<std::endl
     <<" inviter truc machin bidule chose"<<std::endl
     <<" noms truc toto machin bidule chose"<<std::endl
     <<" numero 1 # toto joue en deuxième."<<std::endl
     <<" distribution 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 #J'ai pas d'inspiration"
     <<std::endl
     <<" contrat 1 # truc fait une petite."<<std::endl
     <<" priser 2 # J'attends que toto, malgré son jeu, garde."<<std::endl
     <<" contrat 2 # toto garde."<<std::endl
     <<" contrat 0 # machin passe."<<std::endl
     <<" contrat 0 # bidule passe."<<std::endl
     <<" contrat 0 # chose passe."<<std::endl
     <<" appel #toto doit appeler une carte."<<std::endl
     <<" appeler 27 #J'attends que le client appelle un roi (voir liste)."
     <<std::endl
     <<" contrat_final 1 2 27"<<std::endl
     <<" chien 56 76 77 # le chien contient le petit, l'excuse et le 21."
     <<std::endl
     <<" ecarter 0 1 2 # J'attends que le client écarte le 1, le 2 et le 3."
     <<std::endl
     <<" jeu"
     <<" carte 34 # truc joue une carte."<<std::endl
     <<" jouer 56 # J'attends que toto coupe avec le petit."<<std::endl
     <<" carte 56 # toto joue le petit."<<std::endl
     <<"# Les autres jouent..."<<std::endl
     <<" pli 1 # toto gagne le pli. C'est à lui de jouer."<<std::endl
     <<"# jouer ... # on attend que toto joue '...'."<<std::endl
     <<"# carte ..."<<std::endl
     <<"#..."<<std::endl
     <<" pli 1"<<std::endl
     <<" resultat 10 20 -10 -10 -10"<<std::endl
     <<"# Et ça repart : rotation cyclique des types."<<std::endl
     <<" noms toto machin bidule chose truc"<<std::endl
     <<" numero 0 #Premier à jouer."<<std::endl
     <<" distribution 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14"<<std::endl
     <<" priser 0 #Cette fois-ci, toto passe."<<std::endl
     <<" contrat 0"<<std::endl
     <<" contrat 4"<<std::endl
     <<"#..."<<std::endl;
  return out;
}


QStringList decouper(const QString & contenu)
{
  QStringList mots;
  QString mot_lu;
  int i = 0;

  while(i < contenu.size())
    {
      if(contenu[i] == '#')
	{
	  if(mot_lu != "")
	    {
	      mots << mot_lu;
	      mot_lu.clear();
	    }
	  while(i < contenu.size() && contenu[i] != '\n')
	    i++;
	}
      else if(contenu[i] == ' ' || contenu[i] == '\n'
	      || contenu[i] == '\t')
	{
	  /* Le mot est fini */
	  if(mot_lu != "")
	    {
	      mots << mot_lu;
	      mot_lu.clear();
	    }
	  i++;
	}
      else if(contenu[i] == '\\'
	      && i + 1 < contenu.size())
	{
	  if(contenu[i + 1] == '\\' ||
	     contenu[i + 1] == ' ' ||
	     contenu[i + 1] == '#')
	    {
	      mot_lu.append(contenu[i + 1]);
	      i+=2; /* Le i est réincrémenté plus loin. */
	    }
	  else if(contenu[i + 1] == 'n')
	    {
	      mot_lu.append(QChar('\n'));
	      i+=2;
	    }
	  else if(contenu[i + 1] == 't')
	    {
	      mot_lu.append(QChar('\t'));
	      i+=2;
	    }
	  else if(i + 3 < contenu.size()
		  && contenu[i + 1] >= '0'
		  && contenu[i + 1] <= '9'
		  && contenu[i + 2] >= '0'
		  && contenu[i + 2] <= '9'
		  && contenu[i + 3] >= '0'
		  && contenu[i + 3] <= '9')
	    {
	      int x = 0;
	      for(int j = 0 ; j < 3 ; j++)
		{
		  x *= 10;
		  x += contenu[i + j + 1].unicode() - '0';
		}
	      mot_lu.append(QChar(x));
	      i += 4;
	    }
	  else
	    {
	      mot_lu.append(QChar('?'));
	      i++;
	    }
	}
      else
	{
	  mot_lu.append(contenu[i]);
	  i++;
	}
    }
  if(mot_lu != "")
    mots << mot_lu;
  return mots;
}

enum {_IDENTIFIER, _INVITER, _PRISER, _APPELER, _ECARTER, _JOUER,
      _ERREUR_PROTOCOLE, _REFUSE, _ENTREE, _SORTIE, _NOMS, _NUMERO,
      _DISTRIBUTION, _CONTRAT, _APPEL, _CONTRAT_FINAL, _CHIEN, _ATOUT,
      _JEU, _CARTE, _PLI, _RESULTAT};

int numero_message(const QString & denomination)
{
  QVector<QString> possibilites =
    QVector<QString>()
    <<"identifier"<<"inviter"<<"priser"<<"appeler"<<"ecarter"<<"jouer"
    <<"erreur_protocole"<<"refuse"<<"entree"<<"sortie"<<"noms"<<"numero"
    <<"distribution"<<"contrat"<<"appel"<<"contrat_final"<<"chien"
    <<"atout"<<"jeu"<<"carte"<<"pli"<<"resultat";
  return possibilites.indexOf(denomination);
}

QVector<QStringList> rassembler(const QStringList & mots,
				QStringList & ignored)
{
  QStringList::const_iterator i = mots.constBegin();
  QVector<QStringList> evenements;
  ignored.clear();
  QList<bool> structure; // Contient des true et des false. true :
			 // l'argument doit être un entier.
  while(i != mots.constEnd())
    {
      // On commence par vérifier si les bons arguments sont présents :
      switch(numero_message(*i))
	{
	case _IDENTIFIER:
	  structure = QList<bool>()<<false<<false;
	  break;
	case _INVITER:
	  structure = QList<bool>()<<false<<false<<false<<false<<false;
	  break;
	case _PRISER:
	  structure = QList<bool>()<<false<<true;
	  break;
	case _APPELER:
	  structure = QList<bool>()<<false<<true;
	  break;
	case _ECARTER:
	  structure = QList<bool>()<<false<<true<<true<<true;
	  break;
	case _JOUER:
	  structure = QList<bool>()<<false<<true;
	  break;
	case _ERREUR_PROTOCOLE:
	  structure = QList<bool>()<<false;
	  break;
	case _REFUSE:
	  structure = QList<bool>()<<false;
	  break;
	case _ENTREE:
	  structure = QList<bool>()<<false<<false;
	  break;
	case _SORTIE:
	  structure = QList<bool>()<<false<<false;
	  break;
	case _NOMS:
	  structure = QList<bool>()<<false<<false<<false<<false<<false<<false;
	  break;
	case _NUMERO:
	  structure = QList<bool>()<<false<<true;
	  break;
	case _DISTRIBUTION:
	  structure = QList<bool>()<<false
				   <<QVector<bool>(15, true).toList();
	  break;
	case _CONTRAT:
	  structure = QList<bool>()<<false<<true;
	  break;
	case _APPEL:
	  structure = QList<bool>()<<false;
	  break;
	case _CONTRAT_FINAL:
	  structure = QList<bool>()<<false<<true<<true<<true;
	  break;
	case _CHIEN:
	  structure = QList<bool>()<<false<<true<<true<<true;
	  break;
	case _ATOUT:
	  //std::cout<<"Atout rencontré.\n";
	  if(i + 1 < mots.constEnd())
	    {
	      bool ok = true;
	      (i + 1)->toInt(&ok);
	      if(ok)
		{
		  structure = QList<bool>()<<false<<true;
		  int j = 2; //On considère qu'il y a au moins un atout.
		  while(ok)
		    {
		      //std::cout<<"Test du mot "<<j<<"...\n";
		      ok = i + j < mots.constEnd();
		      /*
		      if(ok)
			{
			  //std::cout<<"C'est "<<(i + j)->toUtf8().data()
			  //<<"...\n"<<std::endl;
			}
		      else
			{
			  //std::cout<<"On a dépassé.\n";
			}
		      //*/
		      ok = ok && j <= 3;
		      /*
		      if(ok)
			{
			  //std::cout<<"On n'en a pas encore eu 3..."<<std::endl;
			}
		      else
			{
			  //std::cout<<"On en a déjà 3.\n"<<std::endl;
			}
		      //*/
		      if(ok)
			{
			  (i + j)->toInt(&ok);
			}
		      if(ok)
			{
			  //std::cout<<"Ajout d'un autre nombre."<<std::endl;
			  structure.append(true);
			  j++;
			}
		      /*
		      else
			{
			  //std::cout<<"Ce n'est plus un nombre."<<std::endl;
			}
		      //*/
		    }
		}
	      else
		{ //Il n'y a pas d'atout, c'est une erreur.
		  structure = QList<bool>();
		}
	    }
	  break;
	case _JEU:
	  structure = QList<bool>()<<false;
	  break;
	case _CARTE:
	  structure = QList<bool>()<<false<<true;
	  break;
	case _PLI:
	  structure = QList<bool>()<<false<<true;
	  break;
	case _RESULTAT:
	  structure = QList<bool>()<<false<<true<<true<<true<<true<<true;
	  break;
	default:
	  structure = QList<bool>();
	}
      if(structure.isEmpty())
	{
	  ignored.append(*i);
	  i++;
	}
      else
	{
	  bool ok = true;
	  QStringList palier;
	  for(QList<bool>::const_iterator argument = structure.constBegin() ;
	      argument < structure.constEnd()
		&& i < mots.constEnd() ; argument++)
	    {
	      if(*argument)
		{
		  //Si on attend un entier
		  i->toInt(&ok);
		  while(!ok && i < mots.constEnd())
		    {
		      ignored.append(*i);
		      i++;
		      if(i < mots.constEnd())
			i->toInt(&ok);
		    }
		  if(ok)
		    {
		      palier.append(*i);
		      i++;
		    }
		}
	      else
		{
		  //Si on n'attend pas un entier
		  palier.append(*i);
		  i++;
		}
	    }
	  if(palier.size() == structure.size())
	    {
	      evenements.append(palier);
	      palier.clear();
	    }
	  else
	    {
	      ignored.append(palier);
	    }
	}
    }
  return evenements;
}

#ifdef TESTS_ECHAPPER

bool tester_decoupage(QString & err, const QString & test_a_decouper,
		      const QStringList & resultat_attendu)
{
  QStringList resultat = decouper(test_a_decouper);
  for(int i = 0 ; i < resultat.size() && i < resultat_attendu.size() ; i++)
    {
      if(resultat[i] != resultat_attendu[i])
	{
	  err += "\"" + (resultat[i]) + "\" (réel) != \""
	    + (resultat_attendu[i]) + "\" (attendu) \n";
	}
    }
  for(int i = resultat.size() ; i < resultat_attendu.size() ; i++)
    {
      err += "Attendu : \"" + resultat_attendu[i] + "\"\n";
    }
  for(int i = resultat_attendu.size() ; i < resultat.size() ; i++)
    {
      err += "Inattendu : \"" + resultat[i] + "\"\n";
    }
  return resultat == resultat_attendu;
}

bool tester_rassemblement(QString & err, const QStringList & test_a_rassembler,
			  const QVector<QStringList> & resultat_attendu,
			  const QStringList & mots_ignores)
{
  QStringList ignores;
  QVector<QStringList> res = rassembler(test_a_rassembler, ignores);
  err = "";
  //Comparaison des résultats :
  for(int i = 0 ; i < res.size() && i < resultat_attendu.size() ; i++)
    {
      if(res[i] != resultat_attendu[i])
	{
	  err += "Résultat : [";
	  for(QStringList::const_iterator j = res[i].constBegin() ;
	      j != res[i].constEnd() ; j++)
	    {
	      err += "\"" + *j + "\"";
	      if(j + 1 != res[i].end())
		{
		  err += " ; ";
		}
	    }
	  err += "] (réel) != [";
	  for(QStringList::const_iterator j = resultat_attendu[i].constBegin() ;
	      j != resultat_attendu[i].constEnd() ; j++)
	    {
	      err += "\"" + *j + "\"";
	      if(j + 1 != resultat_attendu[i].end())
		{
		  err += " ; ";
		}
	    }
	  err += "] (attendu).\n";
	}
      /*
      else
	{
	  err += "Phrase OK : [";
	  for(QStringList::const_iterator j = res[i].constBegin() ;
	      j != res[i].constEnd() ; j++)
	    {
	      err += "\"" + *j + "\"";
	      if(j + 1 != res[i].end())
		{
		  err += " ; ";
		}
	    }
	  err += "].\n";
	}
      //*/
    }
  for(int i = res.size() ; i < resultat_attendu.size() ; i++)
    {
      err += "Phrase attendue : [";
      for(QStringList::const_iterator j = resultat_attendu[i].constBegin() ;
	  j != resultat_attendu[i].constEnd() ; j++)
	{
	  err += "\"" + *j + "\"";
	  if(j + 1 < resultat_attendu[i].end())
	    {
	      err += " ; ";
	    }
	}
      err += "].\n";
    }
  for(int i = resultat_attendu.size() ; i < res.size() ; i++)
    {
      err += "Phrase inattendue : [";
      for(QStringList::const_iterator j = res[i].constBegin() ;
	  j != res[i].constEnd() ; j++)
	{
	  err += "\"" + *j + "\"";
	  if(j + 1 < res[i].end())
	    {
	      err += " ; ";
	    }
	}
      err += "].\n";
    }
  for(int i = 0 ; i < ignores.size() && i < mots_ignores.size() ; i++)
    {
      if(ignores[i] != mots_ignores[i])
	{
	  err += "Mot ignoré : \"" + ignores[i] + "\" (réel) != \""
	    + mots_ignores[i] + "\" (attendu).\n";
	}
    }
  for(int i = ignores.size() ; i < mots_ignores.size() ; i++)
    {
      err += "Mot ignoré attendu : \"" + mots_ignores[i] + "\".\n";
    }
  for(int i = mots_ignores.size() ; i < ignores.size() ; i++)
    {
      err += "Mot ignoré inattendu : \"" + ignores[i] + "\".\n";
    }
  return res == resultat_attendu && ignores == mots_ignores;
}

#endif
